from datetime import datetime
from pathlib import Path
from typing import Dict
import os
from urllib.parse import quote

class TestReporter:
    def __init__(self, output_dir: str = None):
        """初始化测试报告生成器
        
        Args:
            output_dir (str, optional): 自定义报告输出目录. 默认为项目根目录下的artifacts/reports
        """
        # 确定报告输出目录
        project_root = Path(__file__).parent.parent.parent
        self.output_dir = Path(output_dir) if output_dir else project_root / "artifacts" / "reports"
        self._ensure_dir_exists(self.output_dir)
        
        # 初始化测试统计信息
        self.start_time = datetime.now()
        self.cmp_count = 0  # 中医师计数器
        self.reg_count = 0   # 其他专业人员计数器
        
        # 报告数据结构
        self.report_data = {
            "meta": {  # 元信息
                "project": "Professional Registration Validator",
                "start_time": self._get_timestamp(),
                "environment": "Windows 10/Chrome 120"
            },
            "statistics": {  # 统计信息
                "total": 0,
                "passed": 0,
                "failed": 0,
                "skipped": 0,
                "pass_rate": "0%",
                "duration": "0s"
            },
            "cases": []  # 测试用例详情
        }

    def _ensure_dir_exists(self, path: Path):
        """确保目录存在，不存在则创建
        
        Args:
            path (Path): 要检查/创建的目录路径
        """
        path.mkdir(parents=True, exist_ok=True)

    def _get_timestamp(self, fmt: str = "%Y%m%d_%H%M%S") -> str:
        """获取格式化时间戳
        
        Args:
            fmt (str, optional): 时间格式. 默认为"%Y%m%d_%H%M%S"
            
        Returns:
            str: 格式化后的时间字符串
        """
        return datetime.now().strftime(fmt)

    def add_case(self, **kwargs):
        """添加测试用例到报告中
        
        Args:
            **kwargs: 测试用例的各种属性，包括：
                - category: 专业类别
                - type: 测试类型(valid/invalid)
                - test_data: 测试数据
                - actual: 实际结果
                - status: 测试状态(PASS/FAIL)
                - screenshot: 截图路径
                - pattern: 正则表达式规则
                - cm_type: 中医师类型(仅当category为中医师时有效)
        """
        # 生成Case ID
        category = kwargs.get('category', '')
        is_cmp = "Listed or Registered Chinese Medicine Practitioner" in category
        
        if is_cmp:
            self.cmp_count += 1
            case_id = f"TC-CMP-{self.cmp_count:04d}"  # 中医师用例编号
        else:
            self.reg_count += 1
            case_id = f"TC-REG-{self.reg_count:04d}"  # 其他专业人员用例编号

        # 确定数据类型(正向测试/负向测试)
        data_type = "Positive Testing" if kwargs.get('type') == 'valid' else "Negative Testing"
        
        # 构建测试内容
        if category == "Listed or Registered Chinese Medicine Practitioner":
            test_content = f"Professional Category: {category}\n"
            test_content += f"Type of CMP: {kwargs.get('cm_type', '')}\n"
            test_content += f"RegExp Rule: {kwargs.get('pattern', '')}"
        else:
            test_content = f"Professional Category: {category}\n"
            test_content += f"RegExp Rule: {kwargs.get('pattern', '')}"
        
        # 确定预期结果
        expected = "No error messages" if kwargs.get('type') == 'valid' else "Prompt an error message"
        
        # 构建用例数据
        case = {
            "id": case_id,
            "test_content": test_content,
            "type": data_type,
            "test_data": str(kwargs.get("test_data", "")),
            "expected": expected,
            "actual": kwargs.get("actual", ""),
            "status": kwargs.get("status", "PASS").upper(),
            "screenshot": kwargs.get("screenshot", ""),
            "timestamp": self._get_timestamp("%H:%M:%S"),
            "category": category,
            "pattern": kwargs.get("pattern", ""),
            "cm_type": kwargs.get("cm_type", "")
        }
        
        # 添加到用例列表并更新统计
        self.report_data["cases"].append(case)
        self._update_statistics()
        
        # 控制台输出用例信息
        cm_type = kwargs.get('cm_type', 'N/A')
        test_data = kwargs.get('test_data', '')
        status = case['status']
        print(f"\n{case_id} | {category} | {cm_type} | {data_type} | {test_data} | {status}")

    def _update_statistics(self):
        """更新整体统计信息"""
        total = len(self.report_data['cases'])
        passed = sum(1 for case in self.report_data['cases'] if case['status'] == 'PASS')
        failed = sum(1 for case in self.report_data['cases'] if case['status'] == 'FAIL')
        
        self.report_data['statistics'] = {
            "total": total,
            "passed": passed,
            "failed": failed,
            "skipped": total - passed - failed,
            "pass_rate": f"{passed/total*100:.1f}%" if total > 0 else "0%"
        }

    def generate_report(self) -> Dict[str, str]:
        """生成测试报告
        
        Returns:
            Dict[str, str]: 包含生成的报告路径的字典(目前只包含HTML报告路径)
        """
        # 计算测试时长
        end_time = datetime.now()
        duration = end_time - self.start_time
        self.report_data['statistics']['duration'] = str(duration)
        
        # 如果没有用例数据，添加一个警告用例
        if not self.report_data['cases']:
            self.add_case({
                "category": "WARNING",
                "pattern": "N/A",
                "test_data": "No test cases executed",
                "expected": "At least one test case should be executed",
                "actual": "No test cases were recorded",
                "status": "FAIL"
            })
        
        timestamp = self._get_timestamp()
        
        # 生成HTML报告
        html_path = self._generate_html_report(timestamp)
        
        return {
            "html": str(html_path)
        }

    def _generate_html_report(self, timestamp: str) -> Path:
        """生成HTML格式报告
        
        Args:
            timestamp (str): 时间戳，用于生成唯一文件名
            
        Returns:
            Path: 生成的HTML文件路径
        """
        # 从环境变量获取测试文件名称
        test_file = os.environ.get("TEST_FILE", "all")
        html_path = self.output_dir / f"reporting_{test_file}_{timestamp}.html"
        
        with open(html_path, 'w', encoding='utf-8') as f:
            f.write(self._render_html_template())
        
        # 打印报告路径
        print(f"\nTest report generated: \n{html_path}")
        
        return html_path

    def _generate_category_stats(self) -> Dict:
        """按专业类别和数据类型生成统计信息
        
        Returns:
            Dict: 按专业类别和数据类型分类的统计信息
        """
        stats = {}
        
        for case in self.report_data['cases']:
            # 获取分类键(对于中医师包含类型信息)
            if case['category'] == "Listed or Registered Chinese Medicine Practitioner":
                category_key = f"{case['category']}\nType of CMP: {case['cm_type']}"
            else:
                category_key = case['category']
            
            # 初始化分类统计(如果不存在)
            if category_key not in stats:
                stats[category_key] = {
                    "Positive Testing": {"total": 0, "passed": 0, "failed": 0, "skipped": 0},
                    "Negative Testing": {"total": 0, "passed": 0, "failed": 0, "skipped": 0}
                }
            
            # 更新统计计数
            data_type = case['type']
            stats[category_key][data_type]['total'] += 1
            if case['status'] == 'PASS':
                stats[category_key][data_type]['passed'] += 1
            elif case['status'] == 'FAIL':
                stats[category_key][data_type]['failed'] += 1
            else:
                stats[category_key][data_type]['skipped'] += 1
        
        return stats

    def _render_category_stats_table(self) -> str:
        """渲染专业类别统计表格
        
        Returns:
            str: 专业类别统计表格的HTML字符串
        """
        stats = self._generate_category_stats()
        
        html = """
        <h2>Test Statistics by Professional Category</h2>
        <table>
            <thead>
                <tr>
                    <th>Content</th>
                    <th>Data Type</th>
                    <th>Total</th>
                    <th>Passed</th>
                    <th>Failed</th>
                    <th>Skipped</th>
                </tr>
            </thead>
            <tbody>
        """
        
        for category, data in stats.items():
            # 每个分类占两行(正向测试和负向测试各一行)
            html += f"""
            <tr>
                <td rowspan="2" style="vertical-align: top;"><pre>{category}</pre></td>
                <td>Positive Testing</td>
                <td>{data['Positive Testing']['total']}</td>
                <td>{data['Positive Testing']['passed']}</td>
                <td>{data['Positive Testing']['failed']}</td>
                <td>{data['Positive Testing']['skipped']}</td>
            </tr>
            <tr>
                <td>Negative Testing</td>
                <td>{data['Negative Testing']['total']}</td>
                <td>{data['Negative Testing']['passed']}</td>
                <td>{data['Negative Testing']['failed']}</td>
                <td>{data['Negative Testing']['skipped']}</td>
            </tr>
            <tr><td colspan="6" style="border-bottom: 2px solid #ddd;"></td></tr>
            """
        
        html += """
            </tbody>
        </table>
        """
        
        return html

    def _render_html_template(self) -> str:
        """渲染HTML报告模板
        
        Returns:
            str: 完整的HTML报告字符串
        """
        return f"""<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{self.report_data['meta']['project']} - Test Report</title>
    <style>
        body {{ font-family: Arial, sans-serif; margin: 20px; line-height: 1.6 }}
        h1, h2 {{ color: #2c3e50 }}
        table {{ width: 100%; border-collapse: collapse; margin: 15px 0 }}
        th, td {{ border: 1px solid #ddd; padding: 8px 12px; text-align: left }}
        th {{ background-color: #f2f2f2; font-weight: bold }}
        .pass {{ color: #27ae60 }}
        .fail {{ color: #e74c3c; font-weight: bold }}
        .skipped {{ color: #f39c12 }}
        .stat-card {{ 
            display: inline-block; 
            padding: 10px 15px; 
            margin: 5px; 
            border-radius: 5px;
            font-weight: bold 
        }}
        .passed-card {{ background-color: #d5f5e3 }}
        .failed-card {{ background-color: #fadbd8 }}
        .skipped-card {{ background-color: #fdebd0 }}
        pre {{ margin: 0; font-family: Consolas, monospace }}
        
        .screenshot-link {{
        color: #2980b9;
        text-decoration: none;
        font-family: monospace;
        }}
        .screenshot-link:hover {{
        text-decoration: underline;
        color: #3498db;
        }}
    </style>
</head>
<body>
    <h1>{self.report_data['meta']['project']}</h1>
    <p><strong>Execution Time:</strong> {self.report_data['meta']['start_time']}</p>
    <p><strong>Test Environment:</strong> {self.report_data['meta']['environment']}</p>
    <p><strong>Test Duration:</strong> {self.report_data['statistics']['duration']}</p>
    
    <h2>Test Summary</h2>
    <div>
        <span class="stat-card passed-card">Passed: {self.report_data['statistics']['passed']}</span>
        <span class="stat-card failed-card">Failed: {self.report_data['statistics']['failed']}</span>
        <span class="stat-card skipped-card">Skipped: {self.report_data['statistics']['skipped']}</span>
        <span class="stat-card">Total Cases: {self.report_data['statistics']['total']}</span>
        <span class="stat-card">Pass Rate: {self.report_data['statistics']['pass_rate']}</span>
    </div>
    
    {self._render_category_stats_table()}
    
    <h2>Detailed Test Results</h2>
    <table>
        <thead>
            <tr>
                <th>Case ID</th>
                <th>Test Content</th>
                <th>Data Type</th>
                <th>Test Data</th>
                <th>Expected Result</th>
                <th>Actual Result</th>
                <th>Status</th>
                <th>Screenshot</th>
            </tr>
        </thead>
        <tbody>
            {"".join([self._render_case_row(case) for case in self.report_data['cases']])}
        </tbody>
    </table>
</body>
</html>"""

    def _render_case_row(self, case: Dict) -> str:
        """渲染单个测试用例行
        
        Args:
            case (Dict): 测试用例数据
            
        Returns:
            str: 测试用例行的HTML字符串
        """
        status_class = case['status'].lower()
        return f"""
        <tr class="{status_class}">
            <td>{case['id']}</td>
            <td><pre>{case['test_content']}</pre></td>
            <td>{case.get('type', 'N/A')}</td>
            <td><pre>{case['test_data']}</pre></td>
            <td>{case['expected']}</td>
            <td>{case['actual']}</td>
            <td class="{status_class}">{case['status']}</td>
            <td>{self._render_screenshot(case)}</td>
        </tr>
        """

    def _render_screenshot(self, case: Dict) -> str:
        """渲染截图链接(如果有截图)
        
        Args:
            case (Dict): 测试用例数据
            
        Returns:
            str: 截图链接的HTML字符串
        """
        if not case['screenshot']:
            return "N/A"
            
        # 获取截图文件名
        screenshot_path = Path(case['screenshot'])
        filename = screenshot_path.name
        encoded_filename = quote(filename)  # URL编码
        relative_path = f"../screenshots/{encoded_filename}"  # 构建相对路径
        
        # 返回包含缩略图和文件名的HTML
        return f"""
        <div style="text-align: center;">
            <a href="{relative_path}" target="_blank">
                <img src="{relative_path}" 
                    alt="Screenshot" 
                    style="
                        max-height: 80px;
                        max-width: 120px;
                        border: 1px solid #eee;
                        object-fit: contain;
                    "
                    onerror="this.style.display='none'"  # Hide if image fails to load
                >
            </a>
            <div style="
                font-family: monospace;
                font-size: 0.7em;
                word-break: break-all;
                max-width: 150px;
                margin-top: 2px;
            ">{filename}</div>
        </div>
        """